//CHATTING CLIENT

function _log(string) {
    //document.body.innerHTML += string + "<br/><br/>";
    //
    //alert("string");
    //document.getElementById('dump').innerHTML = string;
}

function ChatClient() {

    ////// Public Interfaces /////////////////////////////////////////

    /**
     * Initializes chat client, should be called before any other else
     **/
    this.initialize = function(username, baseUrl) {
        this._username      = username;
        this._baseUrl       = baseUrl;
        this._chatRequest   = createChatSystemRequest(username);
        this.setListener(null);
        this._chatSystRequest();
    }

    /**
     *  ex :
     *      createRoom("new_room_name", ["health","lifestyle"])
     *      will create a "new_room_name" under root/health/lifestyle
     *      if categories is not specified, then the new room wil be created under root
     */
    this.createRoom = function(roomname, categories) {
        temp = createCreateRoomRequest();
        temp.roomName   = roomname;
        temp.categories = categories;
        this._chatRequest.createRoom.push(temp);
    };

    this.joinRoom = function(roomname) {
        this._chatRequest.joinRoom.push(roomname);
    };

    this.exitRoom = function(roomname) {
        this._chatRequest.exitRoom.push(roomname);
    };

    /**
     * chatToRoom("room1", "hello, everyone")
     * will send "hello, everyone" to "room1"
     */
    this.chatToRoom = function(roomname, chat) {
        temp        = createTargetMsgParams();
        temp.target = roomname;
        temp.msg    = chat;
        this._chatRequest.chatToRoom.push(temp);
    };

    this.kickUser = function(roomname, username) {
        temp            = createRoomUserParams();
        temp.roomName   = roomname;
        temp.userName   = username;
        this._chatRequest.kickUser.push(temp);
    };

    this.banUser = function(roomname, username) {
        temp            = createRoomUserParams();
        temp.roomName   = roomname;
        temp.userName   = username;
        this._chatRequest.banUser.push(temp);
    };

    this.unbanUser = function(roomname, username) {
        temp            = createRoomUserParams();
        temp.roomName   = roomname;
        temp.userName   = username;
        this._chatRequest.unbanUser.push(temp);
    };

    /**
     * setRoomPersistance("room_name", true)
     */
    this.setRoomPersistance = function(roomname, persistance) {
        temp            = createRoomPersistParams();
        temp.roomName   = roomname;
        temp.persist    = persistance;
        this._chatRequest.setRoomPersistance.push(temp);
    };

    this.setRoomOwner = function(roomname, username) {
        temp            = createRoomUserParams();
        temp.roomName   = roomname;
        temp.userName   = username;
        this._chatRequest.setRoomOwner.push(temp);
    };

    this.addFriend = function(username) {
        alert(username);
        this._chatRequest.addFriend.push(username);
    };

    /**
     * chatToUser("dimasgabut", "hello, dimasgabut")
     * will send "hello, dimasgabut" to "dimasgabut"
     */
    this.chatToUser = function(username, chat) {
        temp        = createTargetMsgParams();
        temp.target = username;
        temp.msg    = chat;
        this._chatRequest.chatToUser.push(temp);
    };

    /**
     * info("username", function(user) { alert(user.username); }, function(room) { alert(room.name); });
     */
    this.info = function(target, onReceivedUserCallback, onReceivedRoomCallback) {
        //Sending request
        ajWrap = new AjaxWrap();
        ajWrap.onLoadSuccess = function(ajWrap) {
            val = JSON.parse(ajWrap.xhr.responseText);
            if(val.user) onReceivedUserCallback(val.user); // val.user is UserInfo
            if(val.room) onReceivedRoomCallback(val.room); // val.room is RoomInfo
        };
        ajWrap.openAndSend("GET", this._baseUrl + "/ChatServlet?info=" + encodeURIComponent(target), true, null);
    }

    this.addBot = function(roomName, botName, botType) {
        temp            = createBotInfo();
        temp.botName    = botName;
        temp.botType    = botType;
        temp.roomName   = roomName;
        this._chatRequest.addBot.push(temp);
    }

    this.remBot = function(roomname, username) {
        temp            = createRoomUserParams();
        temp.roomName   = roomname;
        temp.userName   = username;
        this._chatRequest.remBot.push(temp);
    };

    /**
     * Listener is a callback function. you should define your own function in listener.
     * Listener
     */
    this.setListener = function(listener) {
        this._listener = listener || new ChatClientListener();
    };



    ////// Internal workings //////////////////////////////////////////////////

    //Vars
    this._baseUrl       = null;
    this._username      = null;
    this._listener      = null;
    this._chatRequest   = null;
    this._timeoutId     = null;

    this._ownedCategId  = [];
    this._ownedRoomNames= [];
    this._friendsNames  = [];
    this._openedRooms   = [];

    //Functions
    this._chatSystRequest = function() {
        //Initiate update request in chatRequest
        CHAT_CLIENT._chatRequest.ownedCategIds     = CHAT_CLIENT._ownedCategId;
        CHAT_CLIENT._chatRequest.ownedRoomNames    = CHAT_CLIENT._ownedRoomNames;
        CHAT_CLIENT._chatRequest.friendsNames      = CHAT_CLIENT._friendsNames;
        CHAT_CLIENT._chatRequest.openedRooms       = CHAT_CLIENT._openedRooms;

        //Sending request
        ajWrap = new AjaxWrap();
        ajWrap.onLoadSuccess = CHAT_CLIENT._chatSystResponse;
        data = (JSON.stringify(CHAT_CLIENT._chatRequest));
        _log("SENDING....\n" + data);
        ajWrap.openAndSend("POST", CHAT_CLIENT._baseUrl + "/ChatServlet", true, "data=" + encodeURIComponent(data));

        //Create new request
        CHAT_CLIENT._chatRequest = createChatSystemRequest(CHAT_CLIENT._username);
    };

    this._chatSystResponse = function(ajWrap) {
        //Get response JSON
        _log("GETTING ... \n" + ajWrap.xhr.responseText);
        response = JSON.parse(ajWrap.xhr.responseText);

        //Parse

        //Check for any new/updated category
        if(response.updatedCategs) {
            for(key in response.updatedCategs) {
                categInfo = response.updatedCategs[key];
                CHAT_CLIENT._listener.onCategoryUpdated(CHAT_CLIENT, categInfo);

                idx = CHAT_CLIENT._ownedCategId.indexOf(categInfo.id);
                if(idx < 0) CHAT_CLIENT._ownedCategId.push(categInfo.id);
            }
        }

        //Check for any deleted category
        if(response.deletedCategIds) {
            for(key in response.deletedCategIds) {
                CHAT_CLIENT._listener.onCategoryUpdated(CHAT_CLIENT, response.deletedCategIds[key]);
                CHAT_CLIENT._ownedCategId.splice(CHAT_CLIENT._ownedCategId.indexOf(response.deletedCategIds[key]), 1);
            }
        }

        //Check for any new/updated rooms
        if(response.updatedRooms) {
            for(key in response.updatedRooms) {
                CHAT_CLIENT._listener.onRoomUpdated(CHAT_CLIENT, response.updatedRooms[key]);
                idx = CHAT_CLIENT._ownedRoomNames.indexOf(response.updatedRooms[key].name);
                if(idx < 0) CHAT_CLIENT._ownedRoomNames.push(response.updatedRooms[key].name);
            }
        }

        //Check for any new deleted rooms
        if(response.deletedRooms) {
            for(key in response.deletedRooms) {
                CHAT_CLIENT._listener.onRoomRemoved(CHAT_CLIENT, response.deletedRooms[key]);
                CHAT_CLIENT._ownedRoomNames.splice(CHAT_CLIENT._ownedRoomNames.indexOf(response.deletedRooms[key]), 1);
            }
        }

        //Check for any new/updated friends
        if(response.updatedFriends) {
            for(key in response.updatedFriends) {
                friendInfo = response.updatedFriends[key];
                CHAT_CLIENT._listener.onFriendUpdated(CHAT_CLIENT, friendInfo);
                idx = CHAT_CLIENT._friendsNames.indexOf(friendInfo.username);
                if(idx < 0) CHAT_CLIENT._friendsNames.push(friendInfo.username);
            }
        }

        //Check for any deleted friends
        if(response.deletedFriends) {
            for(key in response.deletedFriends) {
                friendName  = response.deletedFriends[key];
                CHAT_CLIENT._listener.onFriendRemoved(CHAT_CLIENT, friendName);
                idx = CHAT_CLIENT._friendsNames.indexOf(friendName);
                if(idx >= 0) CHAT_CLIENT._friendsNames.splice(idx, 1);
            }
        }

        //Check for any new/update opened rooms
        if(response.updatedOpenedRooms) {
            for(key in response.updatedOpenedRooms) {
                fullRoomInfo  = response.updatedOpenedRooms[key];
                idx = CHAT_CLIENT._openedRooms.indexOf(fullRoomInfo.name);
                if(idx < 0) {
                    CHAT_CLIENT._openedRooms.push(fullRoomInfo.name);
                    CHAT_CLIENT._listener.onRoomJoined(CHAT_CLIENT, fullRoomInfo);
                } else {
                    CHAT_CLIENT._listener.onOpenedRoomUpdated(CHAT_CLIENT, fullRoomInfo);
                }
            }
        }

        //Check for any removed opened rooms
        if(response.closedOpenedRooms) {
            for(key in response.closedOpenedRooms) {
                roomName  = response.closedOpenedRooms[key];
                CHAT_CLIENT._listener.onRoomExited(CHAT_CLIENT, roomName);
                idx = CHAT_CLIENT._openedRooms.indexOf(roomName);
                if(idx >= 0) CHAT_CLIENT._openedRooms.splice(idx, 1);
            }
        }

        //Check for any new private messages
        if(response.updatedPvChats) {
            for(key in response.updatedPvChats) {
                messageInfo  = response.updatedPvChats[key];
                CHAT_CLIENT._listener.onPrivateChatUpdated(CHAT_CLIENT, messageInfo);
            }
        }

        //Check for any new error
        if(response.errors) {
            for(key in response.errors) {
                errorInfo  = response.errors[key];
                CHAT_CLIENT._listener.onError(CHAT_CLIENT, errorInfo);
            }
        }

        //create new timeout
        CHAT_CLIENT._timeoutId = setTimeout(CHAT_CLIENT._chatSystRequest, 1000);
    };


    return this;
}

/**
 * Override these functions and set it as listener in chatClient
 */
function ChatClientListener() {
    //Called when a new category is added, or a category is updated in system
    this.onCategoryUpdated      = function(chatclient, categoryInfo) {_log("oncategupdated " + categoryInfo.name + " " + categoryInfo.id);};

    //Called when a category is removed from system
    this.onCategoryRemoved      = function(chatclient, categoryId) {_log("oncategrem");};

    //Called when a new room is added, or a room is updated in system
    this.onRoomUpdated          = function(chatclient, roomInfo) {_log("onroomup");};

    //Called when a room is removed from system
    this.onRoomRemoved          = function(chatclient, roomName) {_log("onroomrem");};

    //Called when a new friend is added, or a friend is updated in system
    this.onFriendUpdated        = function(chatclient, userInfo) {_log("onfriendup");};

    //Called when a friend is removed
    this.onFriendRemoved        = function(chatclient, friendName) {_log("onfriendrem");};

    //Called when you successfully joined a room
    this.onRoomJoined           = function(chatclient, roomInfo) {_log("onroomjoin");};

    //Called when you successfully exited from a room
    this.onRoomExited           = function(chatclient, roomName) {_log("onroomexit");};

    //Called when a private chat is sent to you
    this.onPrivateChatUpdated   = function(chatclient, messageInfo) {_log("onpvchat");};

    //Called when an opened room is updated
    this.onOpenedRoomUpdated    = function(chatclient, roomInfo) {_log("onopenedroomup");};

    //Called when error occured (errorCode will be defined later)
    this.onError                = function(chatclient, errorInfo) {_log("onerror");};

    return this;
}




/*******************************************************
 * STRUCTURES
 *******************************************************/


/**
 * Structure for chat system request
 */
function createChatSystemRequest(username) {
    return {
        //Update request information
        "username"          : username,
        "ownedCategIds"     : [], // array of category id
        "ownedRoomNames"    : [], // array of room names
        "friendsNames"      : [], // array of friend names
        "openedRooms"       : [], // array of room names

        //Sepcific requests
        "createRoom"        : [], // array of CreateRoomRequest
        "joinRoom"          : [], // array of room names
        "exitRoom"          : [], // array of room names
        "chatToRoom"        : [], // array of TargetMsgParams
        "kickUser"          : [], // array of RoomUserParams
        "banUser"           : [], // array of RoomUserParams
        "unbanUser"         : [], // array of RoomUserParams
        "setRoomOwner"      : [], // array of RoomUserParams
        "setRoomPersistance": [], // array of RoomPersistParams
        "addFriend"         : [], // array of usernames
        "chatToUser"        : [], // array of TargetMsgParams
        "addBot"            : [], // array of botRequest
        "remBot"            : [] // array of roomUserParams
    };
}

function createChatSystemResponse() {
    return {
        "updatedCategs"     : [], // array of categoryinfo
        "deletedCategIds"   : [], // array of category id
        "updatedRooms"      : [], // array of roominfo
        "deletedRooms"      : [], // array of room names
        "updatedFriends"    : [], // array of userinfo
        "deletedFriends"    : [], // array of name
        "updatedOpenedRooms": [], // array of RoomInfo
        "closedOpenedRooms" : [], // array of room names
        "updatedPvChats"    : [], // array of messageInfo
        "errors"            : [] // array of errorInfo
    };
}

function createBotRequest() {
    return {
        "botName" : null,
        "roomName": null,
        "type"    : null //int, 0 or 1
    }
}

function createCreateRoomRequest() {
    return {
        "roomName"  : null,
        "categories": [] // category of the room, starting from root (root not included)
    };
}

function createTargetMsgParams() {
    return {
        "target"    : null,
        "msg"       : null
    };
}

function createRoomUserParams() {
    return {
        "roomName"  : null,
        "userName"  : null
    };
}

function createRoomPersistParams() {
    return {
        "roomName"  : null,
        "persist"   : null // true/false
    };
}

function createCategoryInfo() {
    return {
        "id"        : null,
        "name"      : null,
        "parentId"  : null
    };
}

function createRoomInfo() {
    return {
        "name"      : null,
        "owner"     : null, // owner username
        "bans"      : [], // array of username (string)
        "users"     : [], // array of userinfo in that room
        "chats"     : [], // array of messageInfo
        "categoryId": null, // category id
        "bots"      : [], // array of botInfo
        "categs"    : [], // array of category in string (root not included)
        "persist"   : false
    };
}

function createBotInfo() {
    return {
        "name"      : null,
        "type"      : null // (int)
    };
}

function createMessageInfo() {
    return {
        "fromSystem"    : false,
        "window"        : null, //could be user name, or room name
        "senderName"    : null,
        "time"          : null,
        "content"       : null
    };
}

function createUserInfo() {
    return {
        "name"      : null, //name
        "username"  : null,
        "moderating": [], // array of catgory names which this user moderate. hierarchy separated by dot. (health.lifestyle)
        "rooms"     : [], // array of room names which this user has
        "isOnline"  : false // true/false
    };
}

function createErrorInfo() {
    return {
        "id" : 0,
        "msg": null
    };
}


//Global vars that is used
var CHAT_CLIENT = new ChatClient();